// Copyright (c) 2025 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.


#pragma once

#include <cmath>
#include <tuple>

#include "./common.hpp"


#ifdef abs
    #undef abs
#endif


namespace data_basic {

    /**
     * @brief 反转1 字节位序
     *
     * @param b
     * @return constexpr uint8_t
     */
    constexpr uint8_t reverse_u8(uint8_t b) {
        // FROM:
        // https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte
        // LICENSE:
        // CC BY-SA 4.0
        b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
        b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
        b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
        return b;
    }


    constexpr uint16_t reverse_u16(uint16_t b) {
        uint8_t h = b >> 8;
        uint8_t l = b & 0xff;

        h = reverse_u8(h);
        l = reverse_u8(l);
        return (static_cast<uint16_t>(l) << 8) + h;
    }


    constexpr uint32_t pack_u32(uint32_t h, uint32_t l) {
        uint32_t r = (h << 16) + l;
        return r;
    }


    constexpr uint32_t pack_u32(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
        uint32_t r = (a << 24) + (b << 16) + (c << 8) + d;
        return r;
    }


    constexpr uint16_t pack_u16(uint16_t h, uint16_t l) {
        uint16_t r = (h << 8) + l;
        return r;
    }


    constexpr std::tuple<uint16_t, uint16_t> split_u32_to_u16(uint32_t v) {
        return {static_cast<uint16_t>(v >> 16), static_cast<uint16_t>(v)};
    }


    constexpr std::tuple<uint8_t, uint8_t> split_u16_to_u8(uint16_t v) {
        return {static_cast<uint8_t>(v >> 8), static_cast<uint8_t>(v)};
    }


    /**
     * @brief 等比例映射
     *
     * 如果原数据超过了设置的原值域，那么映射后的数据也将超出新值域，映射函数本身不做限幅处理。
     *
     * @tparam V 数据类型
     * @param x 待映射的原始数据
     * @param in_min 原值域下界
     * @param in_max 原值域上界
     * @param out_min 新值域下界
     * @param out_max 新值域上界
     * @return V 映射后的新值
     */
    template <typename V>
    constexpr V value_map(V x, V in_min, V in_max, V out_min, V out_max) {
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }


    template <typename V>
    constexpr V value_constrain(V x, V min, V max) {
        if (x < min) return min;
        if (x > max) return max;
        return x;
    }


    /**
     * @brief 计算绝对差值，自动将返回值提升为对应的无符号类型以防止溢出。
     * 例如：absolute_diff((int8_t)-128, (int8_t)100) 将返回 uint8_t(228)，而不是溢出。
     */
    template <typename V>
    constexpr auto absolute_diff(V a, V b) {
        if constexpr (std::is_unsigned_v<V> || std::is_floating_point_v<V>) {
            // 如果是无符号整数或浮点数，直接算差值
            return (a > b) ? (a - b) : (b - a);
        }
        else {
            // 如果是有符号整数，转换为对应的无符号类型再算，避免溢出
            using U = std::make_unsigned_t<V>;
            // 转换为 unsigned 可以在数学上保证差值计算不会 UB (Undefined Behavior)
            return (a > b)
                     ? (static_cast<U>(a) - static_cast<U>(b))
                     : (static_cast<U>(b) - static_cast<U>(a));
        }
    }


    /**
     * @brief 主要是将中间运算结果缓存下来，稍微加速map 运算，消除一次除法
     *
     * @tparam V
     */
    template <typename V>
    class ValueClamp {
       protected:
        V _limit_min = 0;
        V _limit_max = 0;
        V _mapping_min = 0;
        V _mapping_max = 0;

        V _k = 0;
        V _b = 0;

       public:
        ValueClamp(V limit_min, V limit_max) : _limit_min(limit_min), _limit_max(limit_max) {}

        ValueClamp(V limit_min, V limit_max, V mapping_min, V mapping_max) :
            _limit_min(limit_min), _limit_max(limit_max), _mapping_min(mapping_min), _mapping_max(mapping_max) {}

        ValueClamp() {}

        V limit_min() const { return _limit_min; }

        V limit_max() const { return _limit_max; }

        void set_limit(V min, V max) {
            if (min > max) {
                _limit_min = max;
                _limit_max = min;
            }
            else {
                _limit_max = max;
                _limit_min = min;
            }
        }

        V limit(V value) { return value_constrain(value, _limit_min, _limit_max); }

        V map(V value) { return value * _k + _b; }

        V mapping_min() const { return _mapping_min; }

        V mapping_max() const { return _mapping_max; }

        void set_mapping(V min, V max) {
            if (min > max) {
                _mapping_min = max;
                _mapping_max = min;
            }
            else {
                _mapping_min = min;
                _mapping_max = max;
            }

            _k = (_mapping_max - _mapping_min) / (_limit_max - _limit_min);
            _b = _mapping_min - _limit_min * _k;
        }
    };

}  // namespace data_basic